package com.qf.shiro;

import com.qf.pojo.DtsAdmin;
import com.qf.service.AdminService;
import com.qf.service.PermissionService;
import com.qf.service.RoleService;
import com.qf.util.bcrypt.BCryptPasswordEncoder;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Set;

/**
 * @author zhaojian
 */
public class AdminAuthorizingRealm extends AuthorizingRealm {

    public static final Logger logger = LoggerFactory.getLogger(AdminAuthorizingRealm.class);

    @Autowired
    private AdminService adminService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    /**
     * 用户登录成功后, 对用户进行授权, 授予用户对应的角色和权限(能使用的菜单, 按钮等)
     * @param principal
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //1. 获取认证后的用户对象
        DtsAdmin admin = (DtsAdmin)getAvailablePrincipal(principal);
        
        //2. 从用户对象中获取, 用户拥有的角色数组
        Integer[] roleIds = admin.getRoleIds();

        //3. 根据角色数组到数据库中查询对应的用户角色名称集合
        Set<String> roleNameSet = roleService.queryRolesByIds(roleIds);

        //4. 根据角色数组到数据库中获取对应的权限字符串集合
        Set<String> permSet = permissionService.findPermByRoleIds(roleIds);

        //5. 创建shiro框架的权限对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //6. 将获取到的角色加入到shiro框架中
        info.setRoles(roleNameSet);

        //7. 将获取到的权限字符串集合加入到shiro框架中
        info.setStringPermissions(permSet);

        //8. 返回shiro需要的权限对象
        return info;
    }

    /**
     * 登录用户名密码校验
     * @param token
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1. 获取用户页面输入的用户名和密码
        UsernamePasswordToken upToken = (UsernamePasswordToken)token;
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());

        //2. 判断用户名不为空
        if (StringUtils.isEmpty(username)) {
            throw new AccountException("用户名不能为空");
        }

        //3. 判断密码不为空
        if (StringUtils.isEmpty(password)) {
            throw new AccountException("密码不能为空");
        }

        //4. 根据用户名到数据库查询用户对象
        List<DtsAdmin> adminList = adminService.findAdminByUserName(username);

        //5. 判断数据库查询出的数据是否有问题
        if (adminList == null || adminList.size() == 0) {
            logger.error("找不到用户, userName="+ username);
            throw new UnknownAccountException("找不到指定用户, UserName=" + username);
        }
        if (adminList.size() > 1) {
            logger.error("存在同名账户, 不允许登录, UserName=" + username);
            throw new UnknownAccountException("存在同名账户, UserName=" + username);
        }

        //6. 校验用户输入的密码和数据库中保存的密码是否一致
        DtsAdmin dtsAdmin = adminList.get(0);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        //校验两个密码是否一致, 第一个参数: 明文密码, 第二个参数: 密文密码
        boolean flag = encoder.matches(password, dtsAdmin.getPassword());
        if (!flag) {
            logger.error("密码错误, UserName=" + username);
            throw new UnknownAccountException("密码错误, UserName=" + username);
        }

        //7. 封装shiro框架需要的用户对象交给shiro管理
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, this.getName());
        return info;
    }

}
